package edu.hitsz.application;

import edu.hitsz.aircraft.*;
import edu.hitsz.basic.EnemyObject;
import edu.hitsz.bullet.BaseBullet;
import edu.hitsz.basic.AbstractFlyingObject;
import edu.hitsz.factory.enemy.BossFactory;
import edu.hitsz.factory.enemy.EliteFactory;
import edu.hitsz.factory.enemy.EnemyFactory;
import edu.hitsz.factory.enemy.MobFactory;
import edu.hitsz.prop.*;
import edu.hitsz.factory.prop.BloodPropFactory;
import edu.hitsz.factory.prop.BombPropFactory;
import edu.hitsz.factory.prop.BulletPropFactory;
import edu.hitsz.factory.prop.PropFactory;
import edu.hitsz.setName;
import edu.hitsz.thread.MusicThread;
import edu.hitsz.thread.MusicThreadLoop;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;

/**
 * 游戏主面板，游戏启动
 *
 * @author hitsz
 */
public class Game extends JPanel {

    private int backGroundTop = 0;

    /**
     * Scheduled 线程池，用于任务调度
     */
    private final ScheduledExecutorService executorService;

    /**
     * 时间间隔(ms)，控制刷新频率
     */
    private int timeInterval = 20;

    private final HeroAircraft heroAircraft;
    /** List是集合中的接口，<>用于约束集合中的数据类型 */
    private final List<AbstractAircraft> enemyAircrafts;
    private final List<BaseBullet> heroBullets;
    private final List<BaseBullet> enemyBullets;
    private final List<BaseProp> props;

    /**
     * 屏幕中出现的敌机最大数量
     */
    private int enemyMaxNumber = 5;
    public static boolean BossExist = false;
    private int BossCreateInterval = 300;
    private int scoreMilestone = 0;

    /**
     * 当前得分
     */
    private int score = 0;
    /**
     * 当前时刻
     */
    private int time = 0;

    /**
     * 周期（ms)
     * 指示子弹的发射、敌机的产生频率
     */
    private int cycleDuration = 300;
    private int cycleTime = 0;

    public static int music = 1;

    /**
     * 游戏结束标志
     */
    public static boolean gameOverFlag = false;

    // 游戏难度选择，0为简单，1为普通，2为困难
    private int mode;

    public JFrame setNameFrame = new JFrame("setName");

    MusicThreadLoop bgMusic = new MusicThreadLoop("src/videos/bgm.wav", "src/videos/bgm_boss.wav");


    public Game() {
        heroAircraft = HeroAircraft.getInstance();
        enemyAircrafts = new LinkedList<>();
        heroBullets = new LinkedList<>();
        enemyBullets = new LinkedList<>();
        props = new LinkedList<>();

        /**
         * Scheduled 线程池，用于定时任务调度
         * 关于alibaba code guide：可命名的 ThreadFactory 一般需要第三方包
         * apache 第三方库： org.apache.commons.lang3.concurrent.BasicThreadFactory
         */
        this.executorService = new ScheduledThreadPoolExecutor(4,
                new BasicThreadFactory.Builder().namingPattern("game-action-%d").daemon(true).build());


        //启动英雄机鼠标监听
        new HeroController(this, heroAircraft);

    }

    public abstract class ModeTemplateMethod {
        public final void action() {
            if(music == 1){
                bgMusic.start();
            } else {
                ;
            }

            setFrequency();

            // 定时任务：绘制、对象产生、碰撞判定、击毁及结束判定
            Runnable task = () -> {
                time += timeInterval;
                // 周期性执行（控制频率）
                if (timeCountAndNewCycleJudge()) {
                    // System.out.println(time);
                    createMobAndElite();
                    createBoss();
                    // 飞机射出子弹
                    shootAction();
                }
                // 子弹移动
                bulletsMoveAction();
                // 飞机移动
                aircraftsMoveAction();
                // 道具移动
                propsMoveAction();
                // 撞击检测
                crashCheckAction();
                // 后处理
                postProcessAction();
                //每个时刻重绘界面
                repaint();
                // 游戏结束检查英雄机是否存活
                if (heroAircraft.getHp() <= 0) {
                    // 游戏结束
                    executorService.shutdown();
                    gameOverFlag = true;
                    System.out.println("Game Over!");
                    if (music == 1) {
                        new MusicThread("src/videos/game_over.wav").start();
                    }

                    setNameFrame.setContentPane(new setName().getMainPanel());
                    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
                    setNameFrame.setBounds(((int) screenSize.getWidth() - Main.WINDOW_WIDTH) / 2 + Main.WINDOW_WIDTH/4, Main.WINDOW_HEIGHT/2, Main.WINDOW_HEIGHT/4, Main.WINDOW_WIDTH/4);
                    setNameFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                    setNameFrame.setVisible(true);
                }

            };

            /**
             * 以固定延迟时间进行执行
             * 本次任务执行完成后，需要延迟设定的延迟时间，才会执行新的任务
             */
            executorService.scheduleWithFixedDelay(task, timeInterval, timeInterval, TimeUnit.MILLISECONDS);

        }
        abstract public void setFrequency();
        public void setFrequency(int cycleDuration, int timeInterval) {
            Game.this.cycleDuration = cycleDuration;
            Game.this.timeInterval = timeInterval;
        }

        abstract public void createBoss();

        abstract public void createMobAndElite();
        public void createMobAndElite(double prob, double rate){
            EnemyFactory enemyFactory;

            // 新敌机产生
            if (enemyAircrafts.size() < enemyMaxNumber) {
                double randNum = Math.random();
                if (randNum < prob){
                    enemyFactory = new EliteFactory();
                } else {
                    enemyFactory = new MobFactory();
                }
                EnemyAircraft enemy = enemyFactory.createEnemy(rate);
                enemyAircrafts.add(enemy);
                BombProp.addEnemy((EnemyObject) enemy);
            }
        }

        /**
         * 碰撞检测：
         * 1. 敌机攻击英雄
         * 2. 英雄攻击/撞击敌机
         * 3. 英雄获得补给
         */
        private void crashCheckAction() {
            // TODO 敌机子弹攻击英雄
            for (BaseBullet bullet : enemyBullets) {
                if (bullet.notValid()) {
                    continue;
                }
                if (heroAircraft.notValid()) {
                    // 已被其他子弹击毁的敌机，不再检测
                    // 避免多个子弹重复击毁同一敌机的判定
                    continue;
                }
                if (heroAircraft.crash(bullet)) {
                    // 英雄机撞击到敌机子弹
                    // 英雄机机损失一定生命值
                    heroAircraft.decreaseHp(bullet.getPower());
                    bullet.vanish();
                }
            }

            // 英雄子弹攻击敌机
            for (BaseBullet bullet : heroBullets) {
                if (bullet.notValid()) {
                    continue;
                }
                for (AbstractAircraft enemyAircraft : enemyAircrafts) {
                    if (enemyAircraft.notValid()) {
                        // 已被其他子弹击毁的敌机，不再检测
                        // 避免多个子弹重复击毁同一敌机的判定
                        continue;
                    }
                    if (enemyAircraft.crash(bullet)) {
                        // 敌机撞击到英雄机子弹
                        // 敌机损失一定生命值
                        enemyAircraft.decreaseHp(bullet.getPower());
                        if (music == 1) {
                            new MusicThread("src/videos/bullet_hit.wav").start();
                        }
                        bullet.vanish();
                        if (enemyAircraft.notValid()) {
                            // TODO 获得分数，产生道具补给
                            scoreUpdate((EnemyAircraft) enemyAircraft);
                            if (enemyAircraft instanceof MobEnemy || enemyAircraft instanceof EliteEnemy) {
                                randomCreateProp(enemyAircraft);
                            } else if (enemyAircraft instanceof BossEnemy) {
                                BossExist = false;
                                for (int i = 0; i < 3; i++) {
                                    randomCreateProp(enemyAircraft);
                                }
                            }

                        }
                    }
                    // 英雄机 与 敌机 相撞，均损毁
                    if (enemyAircraft.crash(heroAircraft) || heroAircraft.crash(enemyAircraft)) {
                        enemyAircraft.vanish();
                        heroAircraft.decreaseHp(Integer.MAX_VALUE);
                    }
                }
            }

            // Todo: 我方获得道具，道具生效
            for (BaseProp prop : props) {
                if (prop.notValid()) {
                    continue;
                }
                if (heroAircraft.notValid()) {
                    continue;
                }
                if (heroAircraft.crash(prop)) {
                    prop.Active(heroAircraft);
                    for (AbstractAircraft enemyAircraft : enemyAircrafts) {
                        if (enemyAircraft.notValid()) {
                            // TODO 获得分数，产生道具补给
                            scoreUpdate((EnemyAircraft) enemyAircraft);
                        }
                    }
                }
            }

        }

        /**
         用于随机生成道具
         */
        abstract public void randomCreateProp(AbstractAircraft enemyAircraft);

        public void randomCreateProp(AbstractAircraft enemyAircraft, double prob) {
            PropFactory propFactory;
            double randNum = Math.random();
            double seg = (1-prob) / 3;
            if (randNum <= prob) {

            } else if (randNum <= prob + seg*1) {
                propFactory = new BloodPropFactory();
                props.add(propFactory.createProp(enemyAircraft));
            } else if (randNum <= prob + seg*2){
                propFactory = new BombPropFactory();
                props.add(propFactory.createProp(enemyAircraft));
            } else {
                propFactory = new BulletPropFactory();
                props.add(propFactory.createProp(enemyAircraft));
            }
        }
    }

    public class Easy extends ModeTemplateMethod {
        @Override
        public void setFrequency() {
            setFrequency(600, 40);
        }

        @Override
        public void createBoss() { }

        @Override
        public void createMobAndElite() {
            createMobAndElite(0.2, 1);
        }

        @Override
        public void randomCreateProp(AbstractAircraft enemyAircraft) {
            randomCreateProp(enemyAircraft, 0.1);
        }

    }

    public class Normal extends ModeTemplateMethod {
        @Override
        public void setFrequency() {
            setFrequency(450, 30);
        }
        @Override
        public void createBoss() {
            BossCreateInterval = 300;
            EnemyFactory enemyFactory;
            if (score - scoreMilestone >= BossCreateInterval && BossExist == true) {
                scoreMilestone += BossCreateInterval;
                System.out.println("提高难度！精英机概率：" + (0.3 + scoreMilestone/BossCreateInterval*0.025) + "敌机属性提升倍率：" + (1.1+scoreMilestone/BossCreateInterval*0.05));
            }
            if (score - scoreMilestone >= BossCreateInterval && BossExist == false) {
                BossExist = true;
                scoreMilestone += BossCreateInterval;
                System.out.println("提高难度！精英机概率：" + (0.3 + scoreMilestone/BossCreateInterval*0.025) + "敌机属性提升倍率：" + (1.1+scoreMilestone/BossCreateInterval*0.05));
                enemyFactory = new BossFactory();
                int power = 20 + scoreMilestone/BossCreateInterval*2;
                System.out.println("产生Boss敌机！");
                EnemyAircraft enemy = ((BossFactory)enemyFactory).createEnemy(500, power);
                enemyAircrafts.add(enemy);
                BombProp.addEnemy((EnemyObject) enemy);
            }
        }

        @Override
        public void createMobAndElite() {
            createMobAndElite(0.3 + (int)(scoreMilestone/BossCreateInterval)*0.025, 1.1+(int)(scoreMilestone/BossCreateInterval)*0.05);
        }

        @Override
        public void randomCreateProp(AbstractAircraft enemyAircraft) {
            randomCreateProp(enemyAircraft, 0.4);
        }
    }

    public class Hard extends ModeTemplateMethod {
        @Override
        public void setFrequency() {
            setFrequency(300, 20);
        }
        @Override
        public void createBoss() {
            BossCreateInterval = 250;
            EnemyFactory enemyFactory;
            if (score - scoreMilestone >= BossCreateInterval && BossExist == true) {
                scoreMilestone += BossCreateInterval;
                System.out.println("提高难度！精英机概率：" + (0.4 + scoreMilestone/BossCreateInterval*0.05) + "敌机属性提升倍率：" + (1.2+scoreMilestone/BossCreateInterval*0.1));
            }
            if (score - scoreMilestone >= BossCreateInterval && BossExist == false) {
                BossExist = true;
                scoreMilestone += BossCreateInterval;
                System.out.println("提高难度！精英机概率：" + (0.4 + scoreMilestone/BossCreateInterval*0.05) + "敌机属性提升倍率：" + (1.2+scoreMilestone/BossCreateInterval*0.1));
                int hp = 500 + scoreMilestone/BossCreateInterval*100;
                int power = 20 + scoreMilestone/BossCreateInterval*2;
                enemyFactory = new BossFactory();
                System.out.println("产生Boss敌机，Boss敌机血量倍率：" + ((scoreMilestone/BossCreateInterval-1)*0.2 + 1));
                EnemyAircraft enemy = ((BossFactory)enemyFactory).createEnemy(hp, power);
                enemyAircrafts.add(enemy);
                BombProp.addEnemy((EnemyObject) enemy);
            }
        }

        @Override
        public void createMobAndElite() {
            createMobAndElite(0.4 + (int)(scoreMilestone/BossCreateInterval)*0.05, 1.2+(int)(scoreMilestone/BossCreateInterval)*0.1);
        }

        @Override
        public void randomCreateProp(AbstractAircraft enemyAircraft) {
            randomCreateProp(enemyAircraft, 0.7);
        }
    }

    public void setMode(int mode) {
        this.mode = mode;
    }

    public int getMode() {
        return mode;
    }

    /**
     * 游戏启动入口，执行游戏逻辑
     */

    //***********************
    //      Action 各部分
    //***********************

    private boolean timeCountAndNewCycleJudge() {
        cycleTime += timeInterval;
        if (cycleTime >= cycleDuration && cycleTime - timeInterval < cycleTime) {
            // 跨越到新的周期
            cycleTime %= cycleDuration;
            return true;
        } else {
            return false;
        }
    }

    private void shootAction() {
        // TODO 敌机射击
        for (AbstractAircraft enemy: enemyAircrafts) {
            enemyBullets.addAll(enemy.executeShoot());
            List temp = enemyBullets;
            BombProp.addEnemy((List<EnemyObject>)temp);
        }

        // 英雄射击
        heroBullets.addAll(heroAircraft.executeShoot());
        if (music == 1) {
            new MusicThread("src/videos/bullet.wav").start();
        }
    }

    private void bulletsMoveAction() {
        for (BaseBullet bullet : heroBullets) {
            bullet.forward();
        }
        for (BaseBullet bullet : enemyBullets) {
            bullet.forward();
        }
    }

    private void aircraftsMoveAction() {
        for (AbstractAircraft enemyAircraft : enemyAircrafts) {
            enemyAircraft.forward();
        }
    }

    private void propsMoveAction() {
        for (BaseProp prop : props) {
            prop.forward();
        }
    }



    /**
     * 后处理：
     * 1. 删除无效的子弹
     * 2. 删除无效的敌机
     * <p>
     * 无效的原因可能是撞击或者飞出边界
     */
    private void postProcessAction() {
        enemyBullets.removeIf(AbstractFlyingObject::notValid);
        heroBullets.removeIf(AbstractFlyingObject::notValid);
        enemyAircrafts.removeIf(AbstractFlyingObject::notValid);
        props.removeIf(AbstractFlyingObject::notValid);
    }


    //***********************
    //      Paint 各部分
    //***********************

    /**
     * 重写paint方法
     * 通过重复调用paint方法，实现游戏动画
     *
     * @param  g
     */
    @Override
    public void paint(Graphics g) {
        super.paint(g);

        // 绘制背景,图片滚动
        BufferedImage backgroundImage;
        if (mode==0) {
            backgroundImage = ImageManager.BACKGROUND_IMAGE_EASY;
        } else if (mode==1) {
            backgroundImage = ImageManager.BACKGROUND_IMAGE_NORMAL;
        } else {
            backgroundImage = ImageManager.BACKGROUND_IMAGE_HARD;
        }
        g.drawImage(backgroundImage, 0, this.backGroundTop - Main.WINDOW_HEIGHT, null);
        g.drawImage(backgroundImage, 0, this.backGroundTop, null);
        this.backGroundTop += 1;
        if (this.backGroundTop == Main.WINDOW_HEIGHT) {
            this.backGroundTop = 0;
        }

        // 先绘制子弹，后绘制飞机
        // 这样子弹显示在飞机的下层
        paintImageWithPositionRevised(g, props);
        paintImageWithPositionRevised(g, enemyBullets);
        paintImageWithPositionRevised(g, heroBullets);

        paintImageWithPositionRevised(g, enemyAircrafts);

        g.drawImage(ImageManager.HERO_IMAGE, heroAircraft.getLocationX() - ImageManager.HERO_IMAGE.getWidth() / 2,
                heroAircraft.getLocationY() - ImageManager.HERO_IMAGE.getHeight() / 2, null);

        //绘制得分和生命值
        paintScoreAndLife(g);

    }

    private void paintImageWithPositionRevised(Graphics g, List<? extends AbstractFlyingObject> objects) {
        if (objects.size() == 0) {
            return;
        }

        for (AbstractFlyingObject object : objects) {
            BufferedImage image = object.getImage();
            assert image != null : objects.getClass().getName() + " has no image! ";
            g.drawImage(image, object.getLocationX() - image.getWidth() / 2,
                    object.getLocationY() - image.getHeight() / 2, null);
        }
    }

    private void paintScoreAndLife(Graphics g) {
        int x = 10;
        int y = 25;
        g.setColor(new Color(16711680));
        g.setFont(new Font("SansSerif", Font.BOLD, 22));
        g.drawString("SCORE:" + this.score, x, y);
        y = y + 20;
        g.drawString("LIFE:" + this.heroAircraft.getHp(), x, y);
    }

    public int getScore() {
        return score;
    }

    private void scoreUpdate(EnemyAircraft enemyAircraft) {
        if (enemyAircraft instanceof MobEnemy) {
            score += 10;
        } else if (enemyAircraft instanceof EliteEnemy) {
            score += 20;
        } else {
            score += 50;
        }
    }

}
